/*-
 * Copyright (c) 2012-2013 Shang-Rong Cai. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $ MTBLAS: isa_op2.c    Rev. 0.0.11    2013-10-07T12:06+08:00 $
 */

#include <math.h>
#include <complex.h>

#include <exec_isa.h>

/* swap(%a, %b) */
void op2_swap_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    float buf;
    float *rg_a;
    float *rg_b;

    rg_a = (float*) ra->addr;
    rg_b = (float*) rb->addr;

    buf = *rg_a;
    *rg_a = *rg_b;
    *rg_b = buf;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_swap_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    double buf;
    double *rg_a;
    double *rg_b;

    rg_a = (double*) ra->addr;
    rg_b = (double*) rb->addr;

    buf = *rg_a;
    *rg_a = *rg_b;
    *rg_b = buf;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_swap_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    long double buf;
    long double *rg_a;
    long double *rg_b;

    rg_a = (long double*) ra->addr;
    rg_b = (long double*) rb->addr;

    buf = *rg_a;
    *rg_a = *rg_b;
    *rg_b = buf;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_swap_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    float complex buf;
    float complex *rg_a;
    float complex *rg_b;

    rg_a = (float complex*) ra->addr;
    rg_b = (float complex*) rb->addr;

    buf = *rg_a;
    *rg_a = *rg_b;
    *rg_b = buf;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_swap_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    double complex buf;
    double complex *rg_a;
    double complex *rg_b;

    rg_a = (double complex*) ra->addr;
    rg_b = (double complex*) rb->addr;

    buf = *rg_a;
    *rg_a = *rg_b;
    *rg_b = buf;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_swap_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    long double complex buf;
    long double complex *rg_a;
    long double complex *rg_b;

    rg_a = (long double complex*) ra->addr;
    rg_b = (long double complex*) rb->addr;

    buf = *rg_a;
    *rg_a = *rg_b;
    *rg_b = buf;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


/* %b = %a */
void op2_copy_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    float *rg_a;
    float *rg_b;

    rg_a = (float*) ra->addr;
    rg_b = (float*) rb->addr;

    *rg_b = *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_copy_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    double *rg_a;
    double *rg_b;

    rg_a = (double*) ra->addr;
    rg_b = (double*) rb->addr;

    *rg_b = *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_copy_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    long double *rg_a;
    long double *rg_b;

    rg_a = (long double*) ra->addr;
    rg_b = (long double*) rb->addr;

    *rg_b = *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_copy_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    float complex *rg_a;
    float complex *rg_b;

    rg_a = (float complex*) ra->addr;
    rg_b = (float complex*) rb->addr;

    *rg_b = *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_copy_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    double complex *rg_a;
    double complex *rg_b;

    rg_a = (double complex*) ra->addr;
    rg_b = (double complex*) rb->addr;

    *rg_b = *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_copy_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    long double complex *rg_a;
    long double complex *rg_b;

    rg_a = (long double complex*) ra->addr;
    rg_b = (long double complex*) rb->addr;

    *rg_b = *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


/* %b = abs(%a) */
void op2_fabs_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    float *rg_a;
    float *rg_b;

    rg_a = (float*) ra->addr;
    rg_b = (float*) rb->addr;

    *rg_b = fabsf(*rg_a);

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_fabs_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    double *rg_a;
    double *rg_b;

    rg_a = (double*) ra->addr;
    rg_b = (double*) rb->addr;

    *rg_b = fabs(*rg_a);

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_fabs_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    long double *rg_a;
    long double *rg_b;

    rg_a = (long double*) ra->addr;
    rg_b = (long double*) rb->addr;

    *rg_b = fabsl(*rg_a);

    ra->addr += ra->step;
    rb->addr += rb->step;
}


/* accumulation: %b += %a or %b = %a + %b */
void op2_accum_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    float *rg_a;
    float *rg_b;

    rg_a = (float*) ra->addr;
    rg_b = (float*) rb->addr;

    *rg_b += *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_accum_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    double *rg_a;
    double *rg_b;

    rg_a = (double*) ra->addr;
    rg_b = (double*) rb->addr;

    *rg_b += *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_accum_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    long double *rg_a;
    long double *rg_b;

    rg_a = (long double*) ra->addr;
    rg_b = (long double*) rb->addr;

    *rg_b += *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_accum_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    float complex *rg_a;
    float complex *rg_b;

    rg_a = (float complex*) ra->addr;
    rg_b = (float complex*) rb->addr;

    *rg_b += *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_accum_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    double complex *rg_a;
    double complex *rg_b;

    rg_a = (double complex*) ra->addr;
    rg_b = (double complex*) rb->addr;

    *rg_b += *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_accum_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    long double complex *rg_a;
    long double complex *rg_b;

    rg_a = (long double complex*) ra->addr;
    rg_b = (long double complex*) rb->addr;

    *rg_b += *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


/* deduction: %b -= %a or %b = %b - %a */
void op2_deduct_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    float *rg_a;
    float *rg_b;

    rg_a = (float*) ra->addr;
    rg_b = (float*) rb->addr;

    *rg_b -= *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_deduct_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    double *rg_a;
    double *rg_b;

    rg_a = (double*) ra->addr;
    rg_b = (double*) rb->addr;

    *rg_b -= *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_deduct_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    long double *rg_a;
    long double *rg_b;

    rg_a = (long double*) ra->addr;
    rg_b = (long double*) rb->addr;

    *rg_b -= *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_deduct_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    float complex *rg_a;
    float complex *rg_b;

    rg_a = (float complex*) ra->addr;
    rg_b = (float complex*) rb->addr;

    *rg_b -= *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_deduct_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    double complex *rg_a;
    double complex *rg_b;

    rg_a = (double complex*) ra->addr;
    rg_b = (double complex*) rb->addr;

    *rg_b -= *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_deduct_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    long double complex *rg_a;
    long double complex *rg_b;

    rg_a = (long double complex*) ra->addr;
    rg_b = (long double complex*) rb->addr;

    *rg_b -= *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


/* scaling: %b = %a * %b */
void op2_scale_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    float *rg_a;
    float *rg_b;

    rg_a = (float*) ra->addr;
    rg_b = (float*) rb->addr;

    *rg_b *= *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_scale_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    double *rg_a;
    double *rg_b;

    rg_a = (double*) ra->addr;
    rg_b = (double*) rb->addr;

    *rg_b *= *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_scale_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    long double *rg_a;
    long double *rg_b;

    rg_a = (long double*) ra->addr;
    rg_b = (long double*) rb->addr;

    *rg_b *= *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_scale_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    float complex *rg_a;
    float complex *rg_b;

    rg_a = (float complex*) ra->addr;
    rg_b = (float complex*) rb->addr;

    *rg_b *= *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_scale_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    double complex *rg_a;
    double complex *rg_b;

    rg_a = (double complex*) ra->addr;
    rg_b = (double complex*) rb->addr;

    *rg_b *= *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_scale_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    long double complex *rg_a;
    long double complex *rg_b;

    rg_a = (long double complex*) ra->addr;
    rg_b = (long double complex*) rb->addr;

    *rg_b *= *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


/* %b = sum(vector(%a)) */
void op2_vsum_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    float *rg_a;
    float *rg_b;

    rg_a = (float*) ra->addr;
    rg_b = (float*) rb->addr;

    *rg_b += *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_vsum_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    double *rg_a;
    double *rg_b;

    rg_a = (double*) ra->addr;
    rg_b = (double*) rb->addr;

    *rg_b += *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_vsum_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    long double *rg_a;
    long double *rg_b;

    rg_a = (long double*) ra->addr;
    rg_b = (long double*) rb->addr;

    *rg_b += *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_vsum_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    float complex *rg_a;
    float complex *rg_b;

    rg_a = (float complex*) ra->addr;
    rg_b = (float complex*) rb->addr;

    *rg_b += *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_vsum_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    double complex *rg_a;
    double complex *rg_b;

    rg_a = (double complex*) ra->addr;
    rg_b = (double complex*) rb->addr;

    *rg_b += *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_vsum_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    long double complex *rg_a;
    long double complex *rg_b;

    rg_a = (long double complex*) ra->addr;
    rg_b = (long double complex*) rb->addr;

    *rg_b += *rg_a;

    ra->addr += ra->step;
    rb->addr += rb->step;
}


/* %b = sum(vector(abs(%a))) */
void op2_vabssum_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    float *rg_a;
    float *rg_b;

    rg_a = (float*) ra->addr;
    rg_b = (float*) rb->addr;

    *rg_b += fabsf(*rg_a);

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_vabssum_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    double *rg_a;
    double *rg_b;

    rg_a = (double*) ra->addr;
    rg_b = (double*) rb->addr;

    *rg_b += fabs(*rg_a);

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_vabssum_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    long double *rg_a;
    long double *rg_b;

    rg_a = (long double*) ra->addr;
    rg_b = (long double*) rb->addr;

    *rg_b += fabsl(*rg_a);

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_vabssum_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    float complex *rg_a;
    float *rg_b;

    rg_a = (float complex*) ra->addr;
    rg_b = (float*) rb->addr;

    *rg_b += fabsf(crealf(*rg_a)) + fabsf(cimagf(*rg_a));

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_vabssum_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    double complex *rg_a;
    double *rg_b;

    rg_a = (double complex*) ra->addr;
    rg_b = (double*) rb->addr;

    *rg_b += fabs(creal(*rg_a)) + fabs(cimag(*rg_a));

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_vabssum_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    long double complex *rg_a;
    long double *rg_b;

    rg_a = (long double complex*) ra->addr;
    rg_b = (long double*) rb->addr;

    *rg_b += fabsl(creall(*rg_a)) + fabsl(cimagl(*rg_a));

    ra->addr += ra->step;
    rb->addr += rb->step;
}


/* %b = max(vector(abs(%a))) */
void op2_vabsmax_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    float *rg_a;
    float *rg_b;

    rg_a = (float*) ra->addr;
    rg_b = (float*) rb->addr;

    if (fabsf(*rg_a) > fabsf(*rg_b))
    {
        rb->addr = ra->addr;
    }

    ra->addr += ra->step;
}


void op2_vabsmax_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    double *rg_a;
    double *rg_b;

    rg_a = (double*) ra->addr;
    rg_b = (double*) rb->addr;

    if (fabs(*rg_a) > fabs(*rg_b))
    {
        rb->addr = ra->addr;
    }

    ra->addr += ra->step;
}


void op2_vabsmax_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    long double *rg_a;
    long double *rg_b;

    rg_a = (long double*) ra->addr;
    rg_b = (long double*) rb->addr;

    if (fabsl(*rg_a) > fabsl(*rg_b))
    {
        rb->addr = ra->addr;
    }

    ra->addr += ra->step;
}


/* %b = conj(%a) */
void op2_conjcopy_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    float complex *rg_a;
    float complex *rg_b;

    rg_a = (float complex*) ra->addr;
    rg_b = (float complex*) rb->addr;

    *rg_b = conjf(*rg_a);

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_conjcopy_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    double complex *rg_a;
    double complex *rg_b;

    rg_a = (double complex*) ra->addr;
    rg_b = (double complex*) rb->addr;

    *rg_b = conj(*rg_a);

    ra->addr += ra->step;
    rb->addr += rb->step;
}


void op2_conjcopy_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd)
{
    long double complex *rg_a;
    long double complex *rg_b;

    rg_a = (long double complex*) ra->addr;
    rg_b = (long double complex*) rb->addr;

    *rg_b = conjl(*rg_a);

    ra->addr += ra->step;
    rb->addr += rb->step;
}
